package ms.maplestory.boot.loaders;

import ms.maplestory.boot.ServerConstants;
import ms.maplestory.boot.enums.SetItemBaseStatType;
import ms.maplestory.boot.loaders.container.SetItemInfo;
import ms.maplestory.boot.util.Util;
import lombok.extern.slf4j.Slf4j;
import ms.maplestory.boot.util.XMLApi;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
public class EtcData {

    private static Map<Integer, SetItemInfo> setItemInfos = new HashMap<>();

    public static void generateDatFiles() {
        log.info("Started generating etc data.");
        Util.makeDirIfAbsent(ServerConstants.DAT_DIR + "/etc");
        long start = System.currentTimeMillis();
        loadSetItemFromWz();
        saveSetItemsInfoToDat(ServerConstants.DAT_DIR + "/etc/setItems");
        log.info(String.format("Completed generating etc data in %dms.", System.currentTimeMillis() - start));
    }

    public static void loadSetItemFromWz() {
        String wzDir = ServerConstants.WZ_DIR + "/Etc.wz/SetItemInfo.img.xml";
        File file = new File(wzDir);
        Document doc = XMLApi.getRoot(file);
        Node node = doc;
        List<Node> nodes = XMLApi.getAllChildren(node);
        for (Node mainNode : XMLApi.getAllChildren(nodes.get(0))) {
            Map<String, String> attributes = XMLApi.getAttributes(mainNode);
            String setItemId = attributes.get("name");
            if (setItemId != null) {
                SetItemInfo setItemInfo = new SetItemInfo();
                setItemInfo.setId(Integer.parseInt(setItemId));
                for (Node n : XMLApi.getAllChildren(mainNode)) {
                    attributes = XMLApi.getAttributes(n);
                    switch (attributes.get("name")) {
                        case "setItemName":
                            setItemInfo.setSetItemName(attributes.get("value"));
                            break;
                        case "completeCount":
                            setItemInfo.setCompleteCount(Integer.parseInt(attributes.get("value")));
                            break;
                        case "effectLink":
                            setItemInfo.setEffectLink(attributes.get("value"));
                            break;
                        case "ItemID":
                            for (Node items : XMLApi.getAllChildren(n)) {
                                if ("int".equals(items.getNodeName())) {
                                    setItemInfo.getItemID().add(Integer.parseInt(XMLApi.getNamedAttribute(items, "value")));
                                } else if ("imgdir".equals(items.getNodeName())) {
                                    for (Node item : XMLApi.getAllChildren(items)) {
                                        if ("int".equals(item.getNodeName())) {
                                            setItemInfo.getItemID().add(Integer.parseInt(XMLApi.getNamedAttribute(item, "value")));
                                        }
                                    }
                                }
                            }
                            break;
                        case "Effect":
                            for (Node effects : XMLApi.getAllChildren(n)) {
                                Integer parts = Integer.parseInt(XMLApi.getNamedAttribute(effects, "name"));
                                Map<SetItemBaseStatType, Object> setItemEffect = new HashMap<>();
                                for (Node effect : XMLApi.getAllChildren(effects)) {
                                    String name = XMLApi.getNamedAttribute(effect, "name");
                                    String value = XMLApi.getNamedAttribute(effect, "value");
                                    switch (name) {
                                        case "incPDD":
                                            setItemEffect.put(SetItemBaseStatType.incPDD, Integer.parseInt(value));
                                            break;
                                        case "incMDD":
                                            setItemEffect.put(SetItemBaseStatType.incMDD, Integer.parseInt(value));
                                            break;
                                        case "incSTR":
                                            setItemEffect.put(SetItemBaseStatType.incSTR, Integer.parseInt(value));
                                            break;
                                        case "incDEX":
                                            setItemEffect.put(SetItemBaseStatType.incDEX, Integer.parseInt(value));
                                            break;
                                        case "incINT":
                                            setItemEffect.put(SetItemBaseStatType.incINT, Integer.parseInt(value));
                                            break;
                                        case "incLUK":
                                            setItemEffect.put(SetItemBaseStatType.incLUK, Integer.parseInt(value));
                                            break;
                                        case "incACC":
                                            setItemEffect.put(SetItemBaseStatType.incACC, Integer.parseInt(value));
                                            break;
                                        case "incPAD":
                                            setItemEffect.put(SetItemBaseStatType.incPAD, Integer.parseInt(value));
                                            break;
                                        case "incMAD":
                                            setItemEffect.put(SetItemBaseStatType.incMAD, Integer.parseInt(value));
                                            break;
                                        case "incSpeed":
                                            setItemEffect.put(SetItemBaseStatType.incSpeed, Integer.parseInt(value));
                                            break;
                                        case "incMHP":
                                            setItemEffect.put(SetItemBaseStatType.incMHP, Integer.parseInt(value));
                                            break;
                                        case "incMMP":
                                            setItemEffect.put(SetItemBaseStatType.incMMP, Integer.parseInt(value));
                                            break;
                                        case "incEVA":
                                            setItemEffect.put(SetItemBaseStatType.incEVA, Integer.parseInt(value));
                                            break;
                                    }
                                }
                                setItemInfo.getEffect().put(parts, setItemEffect);
                            }
                            break;
                    }
                }
                setItemInfos.put(Integer.parseInt(setItemId), setItemInfo);
            }
        }

    }

    public static void saveSetItemsInfoToDat(String dir) {
        Util.makeDirIfAbsent(dir);
        for (SetItemInfo setItemInfo : setItemInfos.values()) {
            try (DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream(dir + "/" + setItemInfo.getId() + ".dat"))) {
                dataOutputStream.writeInt(setItemInfo.getId());
                dataOutputStream.writeUTF(setItemInfo.getSetItemName());
                dataOutputStream.writeInt(setItemInfo.getCompleteCount());
                dataOutputStream.writeUTF(setItemInfo.getEffectLink());
                dataOutputStream.writeShort(setItemInfo.getItemID().size());
                for (int i = 0; i < setItemInfo.getItemID().size(); i++) {
                    dataOutputStream.writeInt(setItemInfo.getItemID().get(i));
                }
                dataOutputStream.writeShort(setItemInfo.getEffect().size());
                for (Map.Entry<Integer, Map<SetItemBaseStatType, Object>> parts : setItemInfo.getEffect().entrySet()) {
                    dataOutputStream.writeInt(parts.getKey());
                    dataOutputStream.writeShort(parts.getValue().size());
                    for (Map.Entry<SetItemBaseStatType, Object> effect : parts.getValue().entrySet()) {
                        dataOutputStream.writeShort(effect.getKey().getId());
                        dataOutputStream.writeInt((Integer) effect.getValue());
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
